#include <stdint.h>

#include "arm.h"
#include "string.h"
#include "console.h"
#include "kalloc.h"
#include "trap.h"
#include "timer.h"
#include "spinlock.h"
#include "proc.h"
#include "sd.h"
#include "buf.h"
#include "mogic.h"

struct cpu cpus[NCPU];

#include "test/test_main.h"

/* note that `once_lock` should not be in the BSS segment */
static struct spinlock once_lock = {0};
static int once_inited = 0;

static void once_init() {
    acquire(&once_lock);
    if (once_inited) {
        release(&once_lock);
        return;
    }

    /*
     * Before doing anything else, we need to ensure that all
     * static/global variables start out zero.
     */

    extern char edata[], end[];
    memset(edata, 0, end - edata);

    cprintf("main: CPU %d is responsible for kernel initialization.\n", arm_cpuid());

    console_init();
    irq_init();

    alloc_init();
    check_free_list();
    cprintf("alloc: success. page_count=%llu\n", kmem.page_count);

    proc_init();  // initialize ptable

    sd_init();
    binit();

    cop_init();
    user_init();  // create the first user-level process

    once_inited = 1;
    release(&once_lock);
}

int
main()
{
    // while (arm_cpuid() > 0) {}

    once_init();

    extern char vectors[];
    arm_set_vbar(vectors);
    timer_init();

#ifdef __NAIVE_TEST
    test_main();
#endif

    cprintf("cpu %d: main: init success.\n", arm_cpuid());
    scheduler();

    while (1) ;
}
